<template>
  <div class="main">
    <h1 class="title">生成Select语句</h1>
    <div class="box">
      <div class="box-card">
        <div class="left">
          <div class="h-10">
            <p>选择数据源：</p>
            <el-select
              v-model="data.dataSourceValue"
              style="width: 100%"
              placeholder="请选择数据源"
              @change="handleChangeDataSource"
            >
              <el-option
                v-for="item in listDataSource"
                :key="item.id"
                :label="item.dataSourceName"
                :value="item.id"
              />
            </el-select>
          </div>
          <div class="h-15">
            <p style="margin: 5px 0">分页设置：</p>
            <el-select
              v-model="limitValue"
              style="width: 100%"
              placeholder="请选择"
              @change="handleLimitChange"
            >
              <el-option
                v-for="item in dict.type.select_limit_template"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
            <span class="limit-text">{{ form.limit }}</span>
          </div>
          <div class="h-65">
            <p>数据库结构：</p>
            <div class="source_box">
              <div v-if="data.dataSourceValue">
                <el-input
                  v-model="filterText"
                  placeholder="输入关键字进行过滤"
                />
                <el-tree
                  ref="tree"
                  :props="defaultProps"
                  draggable
                  :check-on-click-node="true"
                  :highlight-current="true"
                  :data="treeData"
                  node-key="key"
                  :filter-node-method="filterNode"
                  lazy
                  :load="loadNode"
                  class="filter-tree"
                  show-checkbox
                  @node-click="handleNodeClick"
                  @check="handleCheck"
                >
                  <span
                    v-if="!node.isLeaf"
                    slot-scope="{ node, data }"
                    class="tree-title"
                  >{{ data.label }}</span>
                  <el-tooltip
                    v-else
                    slot-scope="{ data }"
                    class="item"
                    effect="dark"
                    :content="data.label"
                    placement="top-start"
                  >
                    <span class="tree-title">{{ data.label }}</span>
                  </el-tooltip>
                </el-tree>
              </div>
              <div v-else>
                <el-empty description="选择上方数据源" :image-size="50" />
              </div>
            </div>
          </div>
        </div>

        <div ref="right" class="right">
          <div class="field-header">
            <p>SQL生成</p>
            <div class="fun_btn">
              <el-button
                type="primary"
                size="small"
                @click="handleGenerateSql"
              >生成SQL</el-button>
              <el-button
                type="warning"
                size="small"
                @click="handleRunSql"
              >执行SQL</el-button>
            </div>
          </div>
          <div ref="field" class="field-content">
            <div :style="{ height: editorHeight + 'px' }" class="change-height">
              <editor
                ref="aceEditor"
                v-model="sql"
                width="100%"
                :height="editorHeight + 'px'"
                lang="sql"
                theme="github"
                :options="{
                  enableBasicAutocompletion: true,
                  enableSnippets: true,
                  enableLiveAutocompletion: true,
                  tabSize: 4,
                  fontSize: 16,
                  showPrintMargin: false,
                }"
                @init="editorInit"
              />
            </div>
            <div class="line">
              <span>
                <i
                  class="el-icon-caret-top icon"
                  size
                  @click="handleFold('top')"
                />
                <i
                  class="el-icon-caret-bottom icon"
                  size
                  @click="handleFold('bottom')"
                />
              </span>
            </div>
            <div v-if="tableHeight !== 0">
              <div v-if="sqlError" class="error">
                <p style="color:red;padding: 0 10px;">{{ sqlError }}</p>
              </div>
              <el-table
                v-else-if="tableCol.length > 0"
                class="change-height"
                :data="resultData"
                :style="{ width: '100%', height: tableHeight + 'px',overflow:'auto' }"
                border
              >
                <el-table-column type="index" />
                <el-table-column v-for="item in tableCol" :key="item.columnName" :prop="item.columnName" :label="item.columnLabel">
                  <template slot="header" slot-scope="scope">
                    <el-tooltip class="item" effect="dark" :content="item.columnTypeName" placement="top">
                      <span>{{ item.columnLabel }}</span>
                    </el-tooltip>
                  </template>
                </el-table-column>
              </el-table>
              <div v-else class="empty">
                <el-empty description="请生成Select" :image-size="50" />
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import {
  listDatasourceInfo,
  databaseListDatabaseAndSchema,
  databaseListObjects,
  databaseListDbColumns
} from '@/api/console/datasourceInfo'

import { sqlTaskExecuteSql } from '@/api/console/executeSql'
import Vue from 'vue'

export default {
  name: 'TaskSelectMigrationTwo',
  components: { editor: require('vue2-ace-editor') },
  dicts: ['select_limit_template'],
  props: ['data'],
  data() {
    return {
      tableHeight: null,
      editorHeight: null,
      resultData: [],
      sqlResultDialog: false,
      listTheme: [
        'clouds',
        'clouds_midnight',
        'dracula',
        'chrome',
        'chaos',
        'xcode',
        'monokai',
        'ambiance',
        'dreamweaver',
        'eclipse',
        'github',
        'idle_fingers'
      ],
      listCodeLang: ['pgsql', 'sqlserver', 'mysql', 'sql', 'groovy'],
      form: {
        limit: 'OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;'
      },
      listDataSource: [],
      filterText: '',
      treeData: [],
      defaultProps: {
        children: 'children',
        label: 'label',
        isLeaf: 'leaf'
      },
      databaseName: null,
      fieldNodeList: [],
      sql: '',
      limitValue: 'OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;',
      dataFilter: {
        tables: [],
        fields: []
      },
      parentData: [],
      fieldHeight: null,
      dragging: false,
      startY: null,
      sqlError: null,
      tableCol: []
    }
  },
  watch: {
    filterText(val) {
      this.$refs.tree.filter(val)
    },
    treeData() {
      setTimeout(() => {
        this.deleteCheck()
      }, 50)
    },
    sql(val) {
      Vue.set(this.$props.data, 'sql', val)
    }
  },
  async created() {
    this.getDatabaseList()
  },
  mounted() {
    this.sql = this.$props.data.sql || ''
    this.fieldHeight = this.$refs.field.offsetHeight * 0.5 - 1
    this.tableHeight = this.fieldHeight
    this.editorHeight = this.fieldHeight
    this.editorInit()
  },
  methods: {
    handleNodeClick(data, node, el) {
      data.isExpand = !data.isExpand
      console.log(data.isExpand)
      if (node.level === 3 && !data.isExpand) {
        this.parentData.splice(
          this.parentData.indexOf(`${node.parent.parent.data.label}.${node.data.label}`), 1
        )
      }
      if (node.level === 3 && data.isExpand) {
        this.parentData.push(
          `${node.parent.parent.data.label}.${node.data.label}`
        )
      }
    },
    editorInit() {
      // 初始化
      require('brace/ext/language_tools')
      require('brace/ext/beautify')
      require('brace/ext/error_marker')
      require('brace/ext/searchbox')
      require('brace/ext/split')

      // 循坏加载语言，通过点击按钮切换
      for (let s = 0; s < this.listCodeLang.length; s++) {
        require('brace/snippets/' + this.listCodeLang[s])
      }
      for (let j = 0; j < this.listCodeLang.length; j++) {
        require('brace/mode/' + this.listCodeLang[j])
      }

      // 循坏加载主题，通过点击按钮切换
      for (let i = 0; i < this.listTheme.length; i++) {
        require('brace/theme/' + this.listTheme[i])
      }
    },
    handleLimitChange(_v) {
      this.form.limit = _v
    },
    handleGenerateSql() {
      if (!this.$props.data.dataSourceValue) {
        return this.$message.error('请先选择数据源和字段')
      }
      const tenantIdMenus = [
        ...this.$refs.tree.getCheckedKeys(),
        ...this.$refs.tree.getHalfCheckedKeys()
      ]
      this.dataFilter = {
        tables: [],
        fields: []
      }
      const parentNodes = tenantIdMenus.filter(
        (f) => this.$refs.tree.getNode(f).data.isParent
      )
      parentNodes.forEach((parentKey) => {
        const parentNode = this.$refs.tree.getNode(parentKey)
        this.dataFilter.tables.push(parentKey)

        const fieldsForParent = parentNode.childNodes
          .filter(
            (childNode) =>
              tenantIdMenus.includes(childNode.data.key) && childNode.checked
          )
          .map(
            (childNode) =>
              `${parentKey}.${childNode.data.key} AS ${childNode.data.key}`
          )

        this.dataFilter.fields.push(...fieldsForParent)
      })
      this.sql = this.sqlFormatter.format(`SELECT ${this.dataFilter.fields.join(
        ','
      )}\n FROM ${this.parentData.join(',')}\n
      ${this.form.limit}`)
    },
    async handleRunSql() {
      const that = this
      if (this.sql.length < 10) {
        return this.$message.warning('请先写入SQL')
      }
      this.sqlError = null
      const loading = this.$loading({
        lock: true,
        text: 'Loading',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      })
      const item = this.listDataSource.find(
        (f) => f.id === this.$props.data.dataSourceValue
      )
      const res = await sqlTaskExecuteSql({
        datasourceId: item.id,
        sqlContext: that.sql,
        databaseName: '',
        pageSize: 999
      })
      setTimeout(() => {
        if (res.data.code === 500) {
          that.sqlError = res.data.msg
        } else {
          that.tableCol = res.data.data.columns
          res.data.data.rows.forEach((f) => {
            const obj = {}
            that.tableCol.forEach((j, i) => {
              obj[j.columnName] = f[i]
            })
            that.resultData.push(obj)
          })
          that.$props.data.tableData = that.resultData
        }
        loading.close()
      }, 3000)
    },
    waitLazyLoaded(targetNode) {
      return new Promise((resolve, reject) => {
        targetNode.expand()
        resolve()
      })
    },
    handleCheck(selection) {
      if (!(selection.children && selection.children.length > 0)) {
        const targetNode = this.$refs.tree.getNode(selection)
        this.waitLazyLoaded(targetNode).then(() => {
          setTimeout(() => {
            this.fieldNodeList = this.$refs.tree.getCheckedNodes(true)
          }, 100)
        })
      } else {
        this.fieldNodeList = this.$refs.tree.getCheckedNodes(true)
      }
    },
    async getDatabaseList() {
      const res = await listDatasourceInfo({
        pgNum: 1,
        pgSize: 999,
        dataType: '2'
      })
      this.listDataSource = res.rows
    },
    async handleChangeDataSource(e) {
      this.treeData = []
      this.parentData = []
      try {
        const { data } = await databaseListDatabaseAndSchema({
          id: e
        })
        if (data.code !== 200) {
          return this.$message.error(data.msg)
        }
        data.data.forEach((e) => {
          this.treeData.push({
            key: e.database,
            label: e.database,
            disabled: true,
            isParent: false
          })
        })
      } catch (error) {
        console.info(error)
      }
    },
    async loadNode(node, resolve) {
      let res = null
      const children = []
      if (node.level > 0) {
        if (node.level === 1) {
          this.databaseName = node.data.label
        }
        res = await databaseListObjects({
          id: this.$props.data.dataSourceValue,
          taskMode: 'all',
          databaseName: this.databaseName
        })
      }
      if (node.level === 1) {
        const dictValue = await this.getDicts('appraise_taskmode')
        Object.keys(res.data.data).forEach((e) => {
          const label = dictValue.data.find((f) => f.dictValue === e).dictLabel
          children.push({
            key: e,
            label: label,
            name: label,
            disabled: true,
            isParent: false
          })
        })
      }
      if (node.level === 2) {
        res.data.data[node.data.key].forEach((e) => {
          const name = `${node.data.key}Name`
          children.push({
            key: e[name],
            label: e[name],
            name: e[name],
            parent: node.data.key,
            isParent: true
          })
        })
      }
      if (node.level === 3) {
        const res = await databaseListDbColumns({
          id: this.$props.data.dataSourceValue,
          tableNames: [node.data.key],
          databaseName: this.databaseName,
          isParent: false
        })
        if (res.data.data[node.data.key]) {
          res.data.data[node.data.key].forEach((f) => {
            children.push({ name: f, key: f, label: f, leaf: true })
          })
        }
      }
      resolve(children)
      setTimeout(() => {
        this.deleteCheck()
      }, 50)
    },
    filterNode(value, data) {
      if (!value) return true
      return data.label.indexOf(value) !== -1
    },
    deleteCheck(e) {
      const el = document.querySelectorAll('.is-disabled')
      el.forEach((f) => {
        f.remove()
      })
    },
    deleteField(item) {
      this.$refs.tree.setChecked(item, false)
      this.fieldNodeList = this.$refs.tree.getCheckedNodes(true)
    },
    handleFold(type) {
      const that = this
      if (type === 'top') {
        that.tableHeight = this.$refs.field.offsetHeight
        that.editorHeight = 0
      } else {
        that.tableHeight = 0
        that.editorHeight = this.$refs.field.offsetHeight
      }
    },
    drag(e) {
      e.preventDefault()
      this.dragging = true
      this.startY = e.clientY
      document.addEventListener('mousemove', this.doDrag)
      document.addEventListener('mouseup', this.stopDrag)
    },
    doDrag(event) {
      if (this.dragging) {
        if (event.clientY > this.editorHeight) {
          this.editorHeight =
            this.editorHeight + (event.clientY - this.startY) * -1
        }
      }
    },
    stopDrag() {
      this.dragging = false
      document.removeEventListener('mousemove', this.doDrag)
      document.removeEventListener('mouseup', this.stopDrag)
    }
  }
}
</script>
<style lang="scss" scoped>
.main {
  min-width: 1100px;
  width: 100%;
  height: 100%;
  .title {
    letter-spacing: 2px;
    color: #333;
    font-size: 22px;
    line-height: 1.25;
    font-family: alimama, webfont-notdef;
    font-size: 18px;
    box-sizing: border-box;
    font-weight: 600;
    text-align: left;
  }

  .box {
    width: 100%;
    height: 100%;
    margin: 0 auto;
    padding: 10px;
    display: flex;
    .box-card {
      display: flex;
      border: 1px solid #ccc;
      height: 100%;
      width: 100%;

      .left {
        padding: 10px;
        width: 25%;
        height: 100%;
        max-width: 300px;
        border-right: 1px solid #ccc;

        .h-10 {
          height: 65px;
        }

        .h-15 {
          height: 100px;
        }

        .h-65 {
          height: calc(100% - 65px - 110px);
        }

        p {
          margin: 0;
          margin-bottom: 10px;
          font-size: 13px;
          opacity: 0.8;
          font-weight: 600;
        }

        .limit-text {
          margin: 10px 0;
          font-size: 12px;
          opacity: 0.8;
          display: block;
          padding: 0 5px;
        }

        .source_box {
          width: 100%;
          height: 95%;
          padding: 10px;
          flex: 1;
          overflow: auto;
          border: 1px solid #ccc;

          .filter-tree {
            margin: 10px 0;

            .tree-title {
              color: #666;
              font-size: 14px;
            }
          }
        }
      }
    }

    .right {
      width: 100%;
      .field-header {
        height: 8%;
        padding: 0 15px;
        border-bottom: 1px solid #ccc;
        display: flex;
        align-items: center;
        p {
          width: 30%;
          margin: 10px 0;
        }

        .fun_btn {
          width: 70%;
          text-align: right;
        }
      }
      .field-content {
        width: 100%;
        height: 92%;
      }
    }
  }
}

.line {
  width: 100%;
  height: 2px;
  position: relative;
  text-align: center;
  justify-items: center;
  background-color: #91aaff;
  cursor: n-resize;

  &:hover {
    background-color: #003cff;
  }
  span {
    bottom: 0;
    background-color: #fff;
    border-radius: 5px;
    z-index: 888;
    border: 2px solid #91aaff;
    position: absolute;
    bottom: -7.5px;
    height: 15px;
    .icon {
      font-size: 12px;
      color: #91aaff;
      vertical-align: top;

      &:hover {
        color: #003cff;
        cursor: pointer;
      }
    }

    &:hover {
      border: 2px solid #003cff;
    }
  }
}
</style>
<style scoped>
::v-deep .el-card__body {
  height: 100%;
  display: flex;
  flex-direction: column;
}
</style>
